import copy
import math
import time
from . import lib_MJ as MJ
# import lib_MJ as MJ  # 使用的一些库函数
import logging
import datetime
import itertools

from . import yanchengMJ_V5_PH
from . import yanchengMJ_V5_Qidui
from yanchengMJ import global_variable as gv


def value_t1(card):
    """
    计算出牌的危险度评估值，由该牌转化为t3的概率组成
    :param card:
    :return:
    """
    value = 0
    if card!=-1 and card != 53:
        for e in gv.t1tot3_dict[str(card)]:#gv.t1tot3_dict: {'1': [[[1, 1, 1], [1, 1], [1, 6]], [[1, 2, 3], [2, 3], [1, 2]]],
            v = 1
            for i in range(len(e[1])):   #e[1]需要的牌，e[-1]权重
                v *= gv.T_SELFMO[MJ.convert_hex2index(e[1][i])] * e[-1][i]
            value += v
    return value

def get_score_dict(cards,suits,baida_card,padding=[],max_xts=14,hua_num = 0):
    """
    计算各牌型的评估值
    :param cards: 手牌
    :param suits: 副露
    :param baida_card:  百搭牌
    :param fei_baida: 飞百搭
    :param padding: 填充牌。用于计算：op缺一张牌时，填充-1
    :param max_xts: 允许的最大向听数，否则停止计算，用于处理：op中非平胡牌型的吃碰杠处理，例如十三烂牌型吃碰导致需要计算平胡牌型的出牌评估值，从而导致超时
    :return: score_dict,min_xts ，各出牌的评估值与本轮计算的最小向听数（用于op中对比操作前后时）
    """
    # print(cards)

    #寻找向听数在阈值内的牌型
    PH = yanchengMJ_V5_PH.PingHu(cards=cards, suits=suits, baidaCard=baida_card, padding=padding)
    QD = yanchengMJ_V5_Qidui.Qidui(cards=cards, suits=suits, baida_card=baida_card, padding=padding, hua_num= hua_num)
    #组合信息
    CS_PH = PH.pinghu_CS2()
    CS_QD = QD.qidui_CS()
    # print(CS_QD)

    xts_list = [CS_PH[0][-2], CS_QD[-1]]
    min_xts = min(xts_list)
    # op中吃碰后向听数增加的情况，特别是打非平胡的牌型
    if min_xts>max_xts+1:
        return {cards[-1]:0}, min_xts

    type_list = []  # 需搜索的牌型
    for i in range(2):
        if xts_list[i]-1 <= min_xts:
            type_list.append(i)

    score_list = []
    time_start = time.time()
    time_list = []
    for i in type_list:
        if i == 0:
            search_PH = yanchengMJ_V5_PH.SearchTree_PH(hand=cards, suits=suits, combination_sets=CS_PH, baida_card=baida_card, hua_num=hua_num)
            score_list.append(search_PH.get_discard_score())
        elif i == 1:
            score_list.append(QD.get_discard_score())
        time_list.append(time.time()-time_start-sum(time_list))

    score_dict = {}
    #print("score_list", score_list)
    for score in score_list:
        for key in score.keys():
            if key not in score_dict.keys():
                score_dict[key] = score[key]-float(value_t1(key))/(10**(min_xts+1)/2) # 用来区分相同权重的出牌
            else:
                score_dict[key] += score[key]
    return score_dict,min_xts

def recommend_card(cards=[], suits=[], baida_card=None, discards=[], discards_op=[], remain_num=112,
                   round=0, seat_id=0, hua_num = 0):
    """
    功能：推荐出牌接口
    思路：使用向听数作为牌型选择依据，对最小ｘｔｓ的牌型，再调用相应的牌型类出牌决策
    :param cards: 手牌
    :param suits: 副露
    :param baida_card: 百搭牌
    :param discards: 弃牌
    :param discards_op: 场面副露
    :param remain_num: 剩余牌
    :param round 回合数
    :param seat_id 座位号
    :param hua_num 花牌数
    :return: outCard 推荐出牌
    """

    #if 53 in cards: # 花牌直接扔
    #    return 53

    # global gv.T_SELFMO, gv.LEFT_NUM, gv.TIME_START, gv.RT1, gv.RT2, gv.RT3, gv.ROUND, gv.t2tot3_dict, gv.t1tot3_dict

    gv.ROUND = round
    MJ.KING = baida_card
    gv.TIME_START = time.time()
    # 计算获取概率
    gv.LEFT_NUM, _ = MJ.trandfer_discards(discards=discards, discards_op=discards_op, handcards=cards)
    # gv.LEFT_NUM[MJ.translate16_33(MJ.pre_baida(baida_card))] -= 1
    gv.REMAIN_NUM = max(1,min(sum(gv.LEFT_NUM), remain_num))  #剩余牌的总数

    gv.T_SELFMO = [float(i) / gv.REMAIN_NUM for i in gv.LEFT_NUM]
    gv.RT1 = []
    gv.RT2 = []
    gv.RT3 = []

    # 计算所有可能出牌的评估值
    score_dict,_= get_score_dict(cards = cards, suits = suits,baida_card = baida_card, hua_num = hua_num)
    # print("score_dict", score_dict)
    if score_dict != {}:
        # score_dict_max = score_dict[max(score_dict, key=lambda x: score_dict[x])]
        recommend_card = max(score_dict, key=lambda x: score_dict[x])
    else: # 手牌可能已经胡了，这里出一张牌，一般不可能发生
        recommend_card = cards[-1]
    end = time.time()

    # if end - gv.TIME_START > 3:  # 超时输出
    #     pass
    return recommend_card

def recommend_op(op_card, cards=[], suits=[], baida_card=None, discards=[], discards_op=[], canchi=False,
                 self_turn=False, isHu=False, round=0, hua_num = 0):

    """
    功能：动作决策接口
    思路：使用向听数作为牌型选择依据，对最小ｘｔｓ的牌型，再调用相应的牌型类动作决策
    :param op_card: 操作牌
    :param cards: 手牌
    :param suits: 副露
    :param baida_card: 百搭牌
    :param discards: 弃牌
    :param discards_op: 场面副露
    :param self_turn: 是否是自己回合
    :param canchi: 吃牌权限
    :param fei_baida: 飞百搭数
    :param isHu: 是否胡牌
    :round 轮数
    :hua_num 花牌数
    :return: [],isHu 动作组合牌，是否胡牌
    """
    # print("================================================")
    print("baida ------- ", baida_card)
    # print(cards, isHu)
    if isHu:
        return [], True
        
    # for _ in range(cards.count(53)):
       # cards.remove(53)

    print("cards_before", cards)
    print("suits", suits)

    for i in range(len(cards)):
        if cards[i] == 53:
            cards[i] = baida_card
    
    print("cards_after", cards)

    #print('op_card:',op_card)
    # 更新全局变量
    # global gv.T_SELFMO, gv.LEFT_NUM, gv.t2tot3_dict, gv.t1tot3_dict, gv.TIME_START
    MJ.KING = baida_card
    gv.TIME_START = time.time()
    gv.LEFT_NUM, discards_list = MJ.trandfer_discards(discards=discards, discards_op=discards_op, handcards=cards)
    # gv.LEFT_NUM[MJ.translate16_33(MJ.pre_baida(baida_card))] -= 1
    gv.REMAIN_NUM = sum(gv.LEFT_NUM)
    gv.T_SELFMO = [float(i) / gv.REMAIN_NUM for i in gv.LEFT_NUM]
    gv.RT1 = []
    gv.RT2 = []
    gv.RT3 = []

    # gv.t1tot3_dict = MJ.t1tot3_info(T_selfmo=gv.T_SELFMO, gv.RT1=[], gv.RT2=[], gv.RT3=[])
    # gv.t2tot3_dict = MJ.t2tot3_info(T_selfmo=gv.T_SELFMO, gv.RT1=[], gv.RT2=[], gv.RT3=[])

    #计算操作前评估值
    cards_pre = copy.copy(cards)
    # cards_pre.append(-1) #加入一张0作为下次摸到的牌，并提升一定的概率a
    score_dict_pre,min_xts_pre = get_score_dict(cards = cards_pre,suits = suits,baida_card =  baida_card, padding=[-1], hua_num = hua_num)
    # xts_pre = min
    if score_dict_pre!={}:
        score_pre = max(score_dict_pre.values())   #选一个最大的score值作为操作前
    else:
        score_pre = 0

    #计算操作后的评估值
    #确定可选动作
    set_cards = list(set(cards))
    if self_turn: #自己回合，暗杠或补杠
        for card in set_cards:
            if cards.count(card)==4:
                return [card,card,card,card],False #暗杠必杠
        for suit in suits:
            if suit.count(suit[0])==3 and suit[0] in cards:
                return suit+[suit[0]],False

    else: #其他玩家回合 #明杠，吃碰
        if cards.count(op_card)==3:  #杠
            return [op_card,op_card,op_card,op_card],False

        op_sets = []#可操作的集合
        #print(canchi)
        if canchi:
            # 计算可吃组合
            if op_card<0x30: #字牌不能吃
                rm_sets = [[op_card-2,op_card-1],[op_card-1,op_card+1],[op_card+1,op_card+2]]#所有可能操作的集合
            else:
                rm_sets = []
            for op_set in rm_sets:
                if op_set[0] in cards and op_set[1] in cards:
                    op_sets.append(op_set)
            # 碰
            if cards.count(op_card) >= 2:
                op_sets.append([op_card,op_card])
        else:
            if cards.count(op_card) >= 2:
                op_sets.append([op_card,op_card])

        #print(op_sets)
        score_set = []
        for op_set in op_sets:
            cards_ = copy.copy(cards)
            cards_.remove(op_set[0])
            cards_.remove(op_set[1])

            suits_ = MJ.deepcopy(suits)
            suits_.append(sorted(op_set+[op_card]))
            score_dict,_= get_score_dict(cards=cards_, suits=suits_, baida_card=baida_card,max_xts=min_xts_pre, hua_num=hua_num)
            # max_discard = max(score_dict, key=lambda x: score_dict[x])
            # print ("score_dict",score_dict)
            if score_dict!={}:
                if score_dict.keys() == op_card:  # 过吃过碰
                    continue
                score = max(score_dict.values())
                score_set.append(score)
        if time.time() - gv.TIME_START > 3:
            pass
            # logger.warning("op time out %s", time.time() - gv.TIME_START)
        # print('op_set:',op_sets)
        # print('score_set:',score_set)
        #print(score_pre)
        if score_set==[]:
            return [], False
        else:
            max_score = max(score_set)
            # print max_score, score_pre
            # print('max_score:', max_score, 'score_pre', score_pre)
            if max_score > score_pre*1.05:
                return sorted(op_sets[score_set.index(max_score)] + [op_card]), False
    return [],False